Skip to content

Conversation

rdimitrov
Copy link
Contributor

@rdimitrov rdimitrov commented Aug 22, 2025

This PR serves as a placeholder and brings together all of the changes related to supporting vendor extensions passed through the API. The idea is to have a single place to track and review the work as it comes together (even if it’s still a bit of a work in progress).

Disclaimer:

As I mentioned in Discord, this turned out to be a bit larger than I initially expected. The goal of this PR is to get it opened up early so we can kick off the first round of reviews while I continue refining and adding to it. That way, feedback can start flowing in parallel and help guide the remaining work.

The changes in this PR cover:

  • Updates on the data model
  • Updates on both databases (in-memory and postgresql), added a new table to PSQL for tracking the extensions leaving the server table only for the clean server.json properties
  • Updates the seed.json file (thus the big line change)
  • Adds a seed-migrate tool to help migrating an existing seed file to the new format
  • Updates the seed import mechanism to support this
  • Updates the existing tests - unit, integration and validation tests
  • Adds the publish handler to the OpenAPI spec (it was missing)
  • Fixes linting errors
  • Updates on the API side on (publish, list, get) to support the new format of ServerResponse -
{
  "server": {},
  "x-io.modelcontextprotocol.registry": {}, /*not available for publish since this gets populated by the registry*/
  "x-publisher": {}
}

What's left

  • Test it a bit more since I had it implemented first for mongodb, but then used claude to reimplement it for postgresql
  • Have an initial round of discussions with @domdomegg and @tadasant to make sure the overall direction feels right
  • Talk through what the publishing step should look like, i.e. whether we want a dedicated file to cover the extensions (similar to how we currently use server.json)
  • Ensure we don't degrade the existing test coverage
  • Double-check that everything works as expected and the implementation feels good for all of us
  • Once we’re aligned, decide on the best next step: should we break this down into a few smaller follow-up PRs for easier review, or keep it as one larger PR?

Motivation and Context

Motivated by the discussions in: #284

How Has This Been Tested?

Locally and by running all of the tests

Breaking Changes

Yes, it changes both the API and the format of the seed file

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@codecov-commenter
Copy link

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
@rdimitrov
Copy link
Contributor Author

Asked Claude to suggest how we can split it eventually -

Based on the code changes, this can be split into smaller, more reviewable PRs. Here's my recommended approach:

  Suggested PR Sequence

  PR 1: Core Data Model Refactor

  Foundation - No API changes
  - internal/model/model.go - New types (ServerDetail, RegistryMetadata, ServerRecord, etc.)
  - Helper functions (ValidatePublisherExtensions, ExtractPublisherExtensions, etc.)
  - Keep old types for backwards compatibility during transition
  - Risk: Low - No external API impact

  PR 2: Database Layer Updates

  Storage architecture changes
  - internal/database/database.go - Interface updates
  - internal/database/postgres.go - Two-table implementation
  - internal/database/memory.go - In-memory equivalent
  - Database migrations (002_add_server_extensions.sql)
  - Risk: Medium - Database schema changes
  - Depends on: PR 1

  PR 3: Service Layer Integration

  Business logic updates
  - internal/service/service.go - Interface changes
  - internal/service/registry_service.go - Implementation updates
  - internal/service/fake_service.go - Test service updates
  - Risk: Medium - Core business logic
  - Depends on: PR 1, 2

  PR 4: API Response Format Changes

  Non-breaking API updates
  - internal/api/handlers/v0/servers.go - List/Get endpoints to return ServerResponse
  - OpenAPI spec updates for response format
  - Update existing tests
  - Risk: High - Breaking API changes
  - Depends on: PR 1, 2, 3

  PR 5: Publish Endpoint Implementation

  New functionality
  - internal/api/handlers/v0/publish.go - Extension validation and handling
  - OpenAPI spec for publish endpoint
  - Publish-specific tests
  - Risk: Low - New endpoint, no breaking changes
  - Depends on: PR 1, 2, 3

  PR 6: Migration & Seed Updates

  Data migration support
  - tools/migrate-seed/ - Seed migration tool
  - Updated seed data format
  - tools/validate-examples/ - Validation updates
  - Risk: Low - Tooling and data
  - Depends on: All previous PRs

  Alternative: Feature Flag Approach

⏺ Update Todos
  ⎿  ☒ Analyze the PR structure and dependencies     
     ☒ Identify logical boundaries for PR splitting
     ☐ Consider migration and backwards compatibility
     ☒ Propose PR sequence with dependencies

⏺ If you want to deploy incrementally:
  - Add a feature flag for "extension wrapper format"
  - Deploy the storage changes first (they can store both formats)
  - Gradually switch API endpoints to new format
  - Remove old code once fully migrated

  Benefits of Splitting

  Easier Review:
  - Each PR has a clear, single responsibility
  - Smaller diffs are easier to understand
  - Can focus review effort on specific concerns

  Reduced Risk:
  - Database changes isolated and testable independently
  - API breaking changes clearly identified in PR 4
  - New functionality (publish) separated from changes to existing endpoints

  Parallel Development:
  - Multiple team members can work on different PRs simultaneously
  - Earlier PRs can be merged while later ones are still in review

  Challenges to Consider

  Dependency Chain:
  - PRs must be merged in sequence
  - Later PRs need rebasing if earlier ones change

  Testing:
  - Integration tests may need to span multiple PRs
  - Consider adding temporary compatibility layers

  Coordination:
  - More PRs = more coordination overhead
  - Need clear communication about the sequence

⏺ Update Todos
  ⎿  ☒ Analyze the PR structure and dependencies     
     ☒ Identify logical boundaries for PR splitting
     ☒ Consider migration and backwards compatibility
     ☒ Propose PR sequence with dependencies

⏺ My Recommendation: Start with the 6-PR sequence above. PR 1-3 are the foundation and can be reviewed/merged relatively safely. 
PR 4 is where the breaking changes happen, so it needs the most careful review. The sequence allows for incremental progress while maintaining system stability.

@domdomegg
Copy link
Member

@claude can you take a first pass review of this PR, including validating it against what was agreed in #284?

@domdomegg domdomegg self-requested a review August 23, 2025 03:45
Copy link
Contributor

claude bot commented Aug 23, 2025

Claude encountered an error —— View job

Failed with exit code 128

I'll analyze this and get back to you.

@@ -1,34 +1,45 @@
# Server JSON Examples

_These examples show the PublishRequest format used by the `/v0/publish` API endpoint. Each example includes the `server` specification and optional `x-publisher` extensions for build metadata._
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May want to clarify what exactly counts as server.json: I think it's just the bit under server.

And this broader thing, we don't quite have a name for it (registry.json was suggested here although I'm kinda lukewarm about that name).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could alternatively declare that this is the server.json, but I think @tadasant didn't want that in #284

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good catch 👍 I agree with @tadasant that we should keep the server as it is and have a wrapper or a separate file. A wrapper like the registry(-entry).json is probably better, we can have a registry schema that describes it and reuse/reference the existing one for the server.

I'll fix the docs in a follow up to handle only the server and I can re-introduce these examples once we agree on the format for the whole entry 👍

@@ -15,7 +16,7 @@ import (
// PublishServerInput represents the input for publishing a server
type PublishServerInput struct {
Authorization string `header:"Authorization" doc:"Registry JWT token (obtained from /v0/auth/token/github)" required:"true"`
Body model.PublishRequest
RawBody []byte `body:"raw"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused why we do this - it seems like it could still remain some model, just that we verify that model has no extra keys?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right 👍 I've cleared a bunch of things like that as initially I approached it with a more looser format (in case we allow for trusted vendor extensions like "x-com.anthropic"), but it seems I've missed this. I'll fix it in the follow ups 👍

"strings"
"time"

"github.com/modelcontextprotocol/registry/internal/model"
)

// ReadSeedFile reads seed data from various sources:
// 1. Local file paths (*.json files)
// 2. Direct HTTP URLs to seed.json files
// 1. Local file paths (*.json files) - expects extension wrapper format
Copy link
Member

@domdomegg domdomegg Aug 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe call this 'ServerRecord' format, so the naming across comments and models etc. is aligned?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted, will fix it as part of the follow ups 👍

-- This migration implements the extension wrapper architecture

-- Create server_extensions table for registry/publisher metadata
CREATE TABLE server_extensions (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I think we should probably just keep this in the one table?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I think this is probably my highest priority comment, but even this isn't very high priority.

if it's painful to edit the tables nicely with schema migrations, I'm pretty fine for you to rewrite migration 001 instead of creating migration 002 given we're not properly deployed yet, and I can just wipe the database and reapply.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning is I thought it made sense to keep them in separate tables because it makes it easier to track the server.json as its own standalone object in one place while keeping all the extensions organised in another. This way we can look at each independently without things getting too tangled. That might be too much of course, so happy to keep it simple and have them in a single table for now (we can always split/migrate later) 👍

@@ -0,0 +1,170 @@
package main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think once we have the updated files, we can probably discard this tool? Unless you forsee it being useful in other ways?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen a few discussions where others have stood up compatible registries, so I thought we can have it for some time as it may be useful for them too. If that's not the case I can do a follow up and remove it straight away or we can leave it for a few days and remove it before the go-live? In any case I have the same intention to discard it 😃

@domdomegg
Copy link
Member

My opinion here:

  • I think there are a few minor bits that might be nice to clean up, and there's a little more product requirements work to do around the publishing flow (e.g. server.json vs registry.json ...)
  • But generally the major parts here seem correct and aligned with what was agreed
  • I worry that this is a pretty huge PR that will quickly create a bunch of merge conflicts with ~whatever we do next
  • I am therefore inclined to basically merge this soon, with the expectation we can tidy it up more with those follow-on fixes. And that the cost of having a little more mess by merging early will be lower than the wasted cycles of merge conflicts/trying to modularise something that fundamentally is a large change to the schema etc.

@rdimitrov can you let me know if you'd object to me merging this now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe other somewhat high priority comment would be: we should just delete this file, no need to hang around. git history keeps it anyway for us :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I'll file a follow up 👍

domdomegg
domdomegg previously approved these changes Aug 23, 2025
Copy link
Member

@domdomegg domdomegg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

lmk if you object to a merge!

@rdimitrov rdimitrov changed the title Draft: Allow for vendor extensions via x-publisher Allow for vendor extensions via x-publisher Aug 23, 2025
@rdimitrov
Copy link
Contributor Author

@rdimitrov can you let me know if you'd object to me merging this now?

I think this is reasonable 🙏 I'm more than happy to get this merged now and do follow ups addressing your comments 👍 Thank you for reviewing it so promptly!

Co-authored-by: adam jones <domdomegg+git@gmail.com>
@domdomegg domdomegg merged commit 8d7e471 into modelcontextprotocol:main Aug 23, 2025
3 checks passed
domdomegg added a commit that referenced this pull request Aug 25, 2025
This PR adds a new example to the OpenAPI specification that
demonstrates the publisher extensions feature recently added in #298.

## Changes
- Added \`example_org_server\` example to the OpenAPI spec
- Demonstrates \`x-publisher\` fields for contact email and build
metadata
- Shows organization-specific extensions (\`x-com.example\`) including:
  - Marketplace icon URL
  - Category classification
  - Documentation URL
  - Verified publisher status

This example helps developers understand how to use the new vendor
extensions functionality when publishing servers to the registry.

Co-authored-by: adam jones <adamj+git@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants